package tk.capkiller.sharelinks.biz;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.imageio.ImageIO;

public class EasyImage {

	private Set backGroundColors = new HashSet<Integer>();
	private int toleranz;
	private int colorType;// TYPE_USHORT_GRAY
	private BufferedImage myImage;
	public static Color TRANSPARENT_BGCOLOR = Color.BLACK;
	public static final Point BGCOLOR_TEMPLATE = new Point(20, 3);
	public static final Set<Color> COLOR_NUMBER_SMALL;
	public static final Set<Color> COLOR_NUMBER_CENTER;
	public static final int TOLERANZ_BESTMATCH_SMALL_IMAGE = 10;
	public static final int TOLERANZ_BESTMATCH_CENTER_IMAGE = 40;

	static {
		COLOR_NUMBER_SMALL = new HashSet<Color>();
		COLOR_NUMBER_SMALL.add(new Color(35, 35, 35));
		COLOR_NUMBER_SMALL.add(new Color(44, 44, 44));
		COLOR_NUMBER_SMALL.add(new Color(60, 60, 60));
		COLOR_NUMBER_SMALL.add(new Color(66, 66, 66));
		COLOR_NUMBER_SMALL.add(new Color(85, 85, 85));
		COLOR_NUMBER_SMALL.add(new Color(91, 91, 91));
		COLOR_NUMBER_SMALL.add(new Color(93, 93, 93));
		COLOR_NUMBER_SMALL.add(new Color(94, 94, 94));
		COLOR_NUMBER_SMALL.add(new Color(96, 96, 96));
		COLOR_NUMBER_SMALL.add(new Color(98, 98, 98));
		COLOR_NUMBER_SMALL.add(new Color(103, 103, 103));
		COLOR_NUMBER_SMALL.add(new Color(111, 111, 111));
		COLOR_NUMBER_SMALL.add(new Color(114, 114, 114));
		COLOR_NUMBER_SMALL.add(new Color(115, 115, 115));
		COLOR_NUMBER_SMALL.add(new Color(121, 121, 121));
		COLOR_NUMBER_SMALL.add(new Color(125, 125, 125));
		COLOR_NUMBER_SMALL.add(new Color(127, 127, 127));
		COLOR_NUMBER_SMALL.add(new Color(131, 131, 131));
		COLOR_NUMBER_SMALL.add(new Color(140, 140, 140));
		COLOR_NUMBER_SMALL.add(new Color(163, 163, 163));
		COLOR_NUMBER_SMALL.add(new Color(167, 167, 167));
		COLOR_NUMBER_SMALL.add(new Color(187, 187, 187));
		COLOR_NUMBER_SMALL.add(new Color(189, 189, 189));

		COLOR_NUMBER_CENTER = new HashSet<Color>();
		COLOR_NUMBER_CENTER.add(new Color(100, 94, 68));

	}

	public enum Direction {
		HORICONTAL, VERTICAL
	}

	public static BufferedImage makeEasy(BufferedImage input, int pToleranz,
			int pColorType) {

		BufferedImage output = null;
		output = new EasyImage(input, pToleranz, pColorType).makeEasyIntern();

		CaptchaBreaker.testWriteImage(output,
				"C:\\temp\\makeEasyPublicOUTPUT.gif");
		return output;
	}

	public EasyImage(BufferedImage input, int pToleranz, int pColorType) {
		this.toleranz = pToleranz;
		this.myImage = input;
		this.colorType = pColorType;
		collectBGColors();
	}

	private BufferedImage makeEasyIntern() {
		myImage = reduceColors(myImage, this.colorType);
		collectBGColors();
		makeTransparence();
		return myImage;
	}

	/**
	 * 
	 */
	private void collectBGColors() {
		// top border
		BufferedImage backgroundImage = picBackground(0);
		backGroundColors = EasyImage.countColors(backgroundImage);
		// buttom border
		backgroundImage = picBackground(myImage.getHeight()
				- BGCOLOR_TEMPLATE.y);
		backGroundColors.addAll(EasyImage.countColors(backgroundImage));
	}

	private void makeTransparence() {

		CaptchaBreaker
				.testWriteImage(myImage, "C:\\temp\\transparentInput.gif");
		int w = myImage.getWidth();
		int h = myImage.getHeight();

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				int pixel = myImage.getRGB(x, y);
				if (isPixelInBackground(new Color(pixel))) {
					pixel = TRANSPARENT_BGCOLOR.getRGB();
				}
				myImage.setRGB(x, y, pixel);
			}
		}
		CaptchaBreaker.testWriteImage(myImage,
				"C:\\temp\\transparentOutput.gif");
	}

	private boolean isPixelInBackground(Color pColor) {
		return isPixelInBackground(pColor, toleranz);
	}

	private boolean isPixelInBackground(Color pColor, int pToleranz) {
		return isPixelInColorSet(pColor, backGroundColors, pToleranz);
	}

	public static BufferedImage reduceColors(BufferedImage input, int ColorType) {
		CaptchaBreaker.testWriteImage(input, "C:\\temp\\reduceInput.gif");
		BufferedImage output = new BufferedImage(input.getWidth(),
				input.getHeight(), ColorType);
		output.getGraphics().drawImage(input, 0, 0, null);
		CaptchaBreaker.testWriteImage(output, "C:\\temp\\reduceOutput.gif");
		return output;
	}

	private BufferedImage picBackground(int y) {
		return myImage
				.getSubimage(0, y, BGCOLOR_TEMPLATE.x, BGCOLOR_TEMPLATE.y);
	}

	// ############## static methods ################

	public static boolean isPixelInColorSet(Color pixel, Set<Color> colors,
			int pToleranz) {
		boolean result = false;
		Iterator<Color> itrCol = colors.iterator();
		while (itrCol.hasNext() && result == false) {
			Color nextCol = itrCol.next();
			result = matchColor(nextCol, pixel, pToleranz);
		}
		return result;
	}

	public static boolean matchColor(Color color1, Color color2, int pToleranz) {

		int col1 = color1.getRed() + color1.getGreen() + color1.getBlue();
		int col2 = color2.getRed() + color2.getGreen() + color2.getBlue();
		return matchColorInternal(col1, col2, pToleranz);
	}

	public static boolean matchColor(int colorRGB1, int colorRGB2, int pToleranz) {
		Color color1 = new Color(colorRGB1);
		Color color2 = new Color(colorRGB2);
		return matchColor(color1, color2, pToleranz);
	}

	private static boolean matchColorInternal(int colorRGB1, int colorRGB2,
			int pToleranz) {
		int diff = Math.abs(colorRGB1 - colorRGB2);
		return diff <= pToleranz;
	}

	public static Set<Color> countColors(BufferedImage pImage) {
		Set<Color> colors = new HashSet<Color>();
		int w = pImage.getWidth();
		int h = pImage.getHeight();

		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {
				int pixel = pImage.getRGB(x, y);
				colors.add(new Color(pixel));
			}
		}
		return colors;
	}

	/**
	 * 
	 * @param pImage
	 * @param distanceBetweenLetterAndNumber
	 * @param bgColor
	 * @param pToleranz
	 * @return x position of cut between letter and number
	 */
	public static int splitLetterNumber(BufferedImage pImage,
			int distanceBetweenLetterAndNumber, Color bgColor, int pToleranz) {

		int w = pImage.getWidth();

		for (int x = 0; x < w; x++) {
			int split = EasyImage.countPixelWithOtherColors(pImage.getSubimage(
					x, 0, distanceBetweenLetterAndNumber, pImage.getHeight()),
					bgColor);
			if (split < pToleranz) {
				return x + 1;
			}
		}
		return 0;
	}

	public static Point findColor(BufferedImage image, Point start,
			Color colorToFind, int pToleranz, Direction direction,
			boolean forward) {
		return findColor(image, start, colorToFind, pToleranz, direction,
				forward, true);
	}

	public static Point findColor(BufferedImage image, Point start,
			Color colorToFind, int pToleranz, Direction direction,
			boolean forward, boolean match) {
		int w = image.getWidth();
		int h = image.getHeight();
		Point dimension = new Point(w, h);
		Point pos = new Point(start.x, start.y);
		Direction otherDirection = direction.equals(Direction.HORICONTAL) ? Direction.VERTICAL
				: direction.HORICONTAL;
		int x = 0;
		int y = 0;

		for (int xy1 = getXY(start, otherDirection); conditionLoopFindColor(
				getXY(pos, otherDirection), getXY(dimension, otherDirection),
				forward); xy1 = upPoint(pos, otherDirection, 1, forward)) {
			for (int xy2 = getXY(start, direction); conditionLoopFindColor(
					getXY(pos, direction), getXY(dimension, direction), forward); xy2 = upPoint(
					pos, direction, 1, forward)) {
				// System.out.println(pos.x + "," + pos.y);

				x = pos.x;
				y = pos.y;
				// System.out.println(x + "," + y);
				boolean matchColReslult = matchColor(image.getRGB(x, y),
						colorToFind.getRGB(), pToleranz);
				if ((match && matchColReslult) || (!match && !matchColReslult)) {
					return new Point(x, y);
				}

			}
			resetPointXY(pos, direction, start, dimension, forward);
		}
		return null;
	}

	private static boolean conditionLoopFindColor(int itrI, int maxMin,
			boolean forward) {
		maxMin = forward ? maxMin : 0;
		return forward ? itrI < maxMin : itrI > maxMin;
	}

	public static int getXY(Point p, boolean direction) {
		HashMap<Boolean, Direction> param = new HashMap<Boolean, EasyImage.Direction>();
		param.put(Boolean.TRUE, Direction.HORICONTAL);
		param.put(Boolean.FALSE, Direction.VERTICAL);

		return getXY(p, param.get(direction));

	}

	public static void resetPointXY(Point p, Direction d, Point resetValue,
			Point dimension, boolean forward) {
		switch (d) {
		case HORICONTAL:
			p.x = forward ? 0 : dimension.x - 1;
			break;
		default:
			p.y = forward ? 0 : dimension.y - 1;
		}
	}

	public static int getXY(Point p, Direction d) {
		switch (d) {
		case HORICONTAL:
			return p.x;
		default:
			return p.y;
		}
	}

	public static void upPoint(Point p, boolean direction, int add,
			boolean forward) {
		HashMap<Boolean, Direction> param = new HashMap<Boolean, EasyImage.Direction>();
		param.put(Boolean.TRUE, Direction.HORICONTAL);
		param.put(Boolean.FALSE, Direction.VERTICAL);

		upPoint(p, param.get(direction), add, forward);
	}

	public static int upPoint(Point p, Direction d, int add, boolean forward) {

		add = forward ? add : add * -1;
		switch (d) {
		case HORICONTAL:
			p.x = p.x + add;
			return p.x;
		default:
			p.y = p.y + add;
			return p.y;
		}
	}

	public static int countPixelWithThisColor(BufferedImage pImage,
			Color searchColor) {
		return countPixelColorInternal(pImage, searchColor, true);
	}

	private static int countPixelColorInternal(BufferedImage pImage,
			Color pColor, boolean match) {
		int counter = 0;
		int w = pImage.getWidth();
		int h = pImage.getHeight();
		boolean matchResult;
		for (int y = 0; y < h; y++) {
			for (int x = 0; x < w; x++) {

				matchResult = matchColor(pImage.getRGB(x, y), pColor.getRGB(),
						0);
				if (match && matchResult) {
					counter++;
				}
				if (!match && !matchResult) {
					counter++;
				}
			}
		}
		return counter;
	}

	public static int nextPixelWithOtherColor(BufferedImage pImage, Color pColor) {
		int w = pImage.getWidth();
		for (int x = 0; x < w; x++) {
			if (pImage.getRGB(x, 0) == pColor.getRGB()) {
				return x;
			}
		}
		return 0;
	}

	public static int countPixelWithOtherColors(BufferedImage pImage,
			Color notColor) {

		return countPixelColorInternal(pImage, notColor, false);
	}

	/**
	 * fills every area of size pBlockSize if it has less then 4 Pixels with the
	 * Color newColor and fill this block with toDeleteColor result: lonely
	 * pixels are deleted, something like "noise reduction"
	 * 
	 * @param pImage
	 * @param pBlockSize
	 * @param toDeleteColor
	 * @param newColor
	 */
	public static void cleanImage(BufferedImage pImage, Point pBlockSize,
			Color toDeleteColor, Color newColor) {

		CaptchaBreaker.testWriteImage(pImage,
				"C:\\temp\\methodCleanImageINPUT.gif");

		int w = pImage.getWidth();
		int h = pImage.getHeight();
		int x = 0;
		int y = 0;
		int countColor = 0;
		int[] rgbArray = new int[pBlockSize.x * pBlockSize.y];
		for (int i = 0; i < rgbArray.length; i++) {
			rgbArray[i] = newColor.getRGB();
		}
		// col
		while (x + pBlockSize.x < w) {
			// row
			while (y + pBlockSize.y < h) {
				countColor = countPixelWithThisColor(
						pImage.getSubimage(x, y, pBlockSize.x, pBlockSize.y),
						toDeleteColor);
				if (countColor < (pBlockSize.x * pBlockSize.y)) {
					pImage.setRGB(x, y, newColor.getRGB());
				}
				y++;
			}
			y = 0;
			x++;
		}

	}

	public static BufferedImage isolateImage(BufferedImage pImage,
			Color p_color, Color newColor, Color oldColor, int pToleranz) {
		return isolateImage(pImage, p_color, newColor, oldColor, pToleranz,
				true);
	}

	public static BufferedImage isolateImage(BufferedImage pImage,
			Color newColor, Color newCOlor, Color oldColor, int pToleranz,
			boolean match) {
		Set onlyColors = new HashSet<Color>();
		onlyColors.add(newColor);
		return isolateImage(pImage, onlyColors, newColor, oldColor, pToleranz,
				match);
	}

	public static BufferedImage isolateImage(BufferedImage pImage,
			Set<Color> pColors, Color newColor, Color oldColor, int pToleranz) {
		return isolateImage(pImage, pColors, newColor, oldColor, pToleranz,
				true);
	}

	public static BufferedImage isolateImage(BufferedImage pImage,
			Set<Color> onlyOrNotColors, Color oldColor, Color newColor,
			int pToleranz, boolean match) {

		BufferedImage output = null;
		CaptchaBreaker.testWriteImage(pImage,
				"C:\\temp\\methodIsolateImageINPUT.gif");
		output = new BufferedImage(pImage.getWidth(), pImage.getHeight(),
				pImage.getType());
		int w = pImage.getWidth() - 2;// border
		int h = pImage.getHeight() - 2;// border

		int x = 0;
		int y = 0;

		do {
			do {
				boolean matchResult = isPixelInColorSet(
						new Color(pImage.getRGB(x, y)), onlyOrNotColors,
						pToleranz);

				// System.out.println("isolate: " + x + "/" + y + " "
				// + new Color(pImage.getRGB(x, y)));
				if ((matchResult)) {
					// System.out.println("->> " + newColor);
					output.setRGB(x, y, newColor.getRGB());
				} else if (!matchResult && !match) {
					output.setRGB(x, y, pImage.getRGB(x, y));
				}

				x++;
			} while (x < w);

			x = 0;
			y++;
		} while (y < h);

		CaptchaBreaker.testWriteImage(output,
				"C:\\temp\\methodIsolateImageBeforeClean.gif");
		if (match) {
			cleanImage(output, new Point(2, 2), newColor, oldColor);
			cleanImage(output, new Point(2, 2), oldColor, newColor);

		}

		CaptchaBreaker.testWriteImage(output,
				"C:\\temp\\methodIsolateImageOUTPUT.gif");
		return output;
	}

	public static BufferedImage deleteLetter(BufferedImage input) {
		BufferedImage output = new BufferedImage(input.getWidth(),
				input.getHeight(), input.getType());
		output.createGraphics().drawImage(input, 0, 0, null);
		Point startOfLetter = getCornerLeft(output, Color.BLACK, false); // search
		CaptchaBreaker.testWriteImage(input, "C:\\temp\\deleteLetterInput.gif");
		fill8(output, startOfLetter.x, startOfLetter.y, Color.BLACK.getRGB(),
				Color.BLACK.getRGB(), false);
		output = EasyImage.cutImage(output, Color.BLACK, Color.WHITE);

		CaptchaBreaker.testWriteImage(output,
				"C:\\temp\\deleteLetterOutput.gif");
		return output;
	}

	/**
	 * 
	 * @param pInputImage
	 *            image to edit
	 * @param keepColor
	 *            color to keep alive
	 * @param deleteColor
	 *            color to delete
	 * @param direction
	 *            Direction.HORICONTAL, Direction.VERTICAL
	 * @param maxDistance
	 *            max. Size of leak to fill (distance between point 1 and point
	 *            2)
	 * @param repeat
	 *            BE CAREFULLY! its very expensive!
	 * @return
	 */
	public static BufferedImage fillLeaks(BufferedImage pInputImage,
			Color keepColor, Color deleteColor, Direction direction,
			int maxDistance, int repeat) {

		if (repeat > 4) {
			throw new IllegalArgumentException("param 'repeat': " + repeat
					+ "is too high, maximum allowed: 4");
		}
		// System.out.println("fillLeaks (distance: " + maxDistance +
		// ", repeat: "
		// + repeat + ")...");
		int r = 0;
		while (r < repeat) {

			Point pos1 = new Point(0, 0);
			Point pos2 = new Point(0, 0);

			while (pos2 != null && pos1 != null
					&& pos2.x < pInputImage.getWidth()
					&& pos2.y < pInputImage.getHeight()) {
				pos1 = findColor(pInputImage, new Point(pos1.x + 1, pos2.y),
						keepColor, 0, direction, true);
				if (pos1 != null) {
					pos1 = findColor(pInputImage, pos1, deleteColor, 0,
							direction, true);
					if (pos1 != null) {// NEW debug todo test
						pos2 = findColor(pInputImage, pos1, keepColor, 0,
								direction, true);
					}
					pInputImage.getGraphics().setColor(Color.RED);
				}

				if (pos1 == null || pos2 == null
						|| Math.abs(pos1.distance(pos2)) > maxDistance) {

					continue;
				}
				pInputImage.getGraphics().drawLine(pos1.x, pos1.y, pos2.x,
						pos2.y);
				pos1.x = 0;
				pos1.y = 0;
				pos2.x = 0;
				pos2.y = 0;

			}
			r++;
		}

		return pInputImage;
	}

	/**
	 * 
	 * @param pImage
	 * @param bgColor
	 * @param mainColor
	 * @param splitX
	 * @param splitY
	 * @return
	 */
	// public static Point[] getCorners(BufferedImage pImage, Color bgColor,
	// Color mainColor, int splitX, int splitY) {
	// Point topLeft = new Point();
	// Point topRight = new Point();
	// Point buttomLeft = new Point();
	// Point buttomRight = new Point();
	//
	// topLeft = getCornerLeft(pImage.getSubimage(0, 0, splitX, splitY),
	// bgColor, mainColor);
	// // ------------------------------------------
	//
	// topRight = getCornerTop(pImage.getSubimage(splitX, 0, pImage.getWidth()
	// - splitX, splitY), bgColor, mainColor);
	// topRight.x += splitX;
	//
	// // ------------------------------------------
	//
	// buttomLeft = getCornerButtom(
	// pImage.getSubimage(0, splitY, splitX, pImage.getHeight()
	// - splitY), bgColor, mainColor);
	// buttomLeft.y += splitY;
	//
	// // ------------------------------------------
	// buttomRight = getCornerButtom(pImage.getSubimage(splitX, splitY,
	// pImage.getWidth() - splitX, pImage.getHeight() - splitY),
	// bgColor, mainColor);
	// buttomRight.x += splitX;
	// buttomRight.y += splitY;
	//
	// System.out.println(topLeft.x + "," + topLeft.y + " ---------- "
	// + topRight.x + "," + topRight.y);
	// System.out.println("     |----------|    ");
	// System.out.println("     |----------|    ");
	// System.out.println("     |----------|    ");
	// System.out.println(buttomLeft.x + "," + buttomLeft.y + " ---------- "
	// + buttomRight.x + "," + buttomRight.y);
	//
	// return new Point[] { topLeft, topRight, buttomLeft, buttomRight };
	//
	// }

	/**
	 * 
	 * @param pImage
	 * @param bgColor
	 * @param mainColor
	 * @param splitX
	 * @param splitY
	 * @return
	 */

	public static Point[] getCorners(BufferedImage pImage, Color bgColor,
			Color mainColor) {

		CaptchaBreaker
				.testWriteImage(pImage, "C:\\temp\\getCornersForThis.gif");
		Point left = getCornerLeft(pImage, bgColor, false);
		Point right = getCornerRight(pImage, bgColor, false);
		Point top = getCornerTop(pImage, bgColor, false);
		Point buttom = getCornerButtom(pImage, bgColor, false);
		if (left.x == 1) {
			left.x = 0;
		}
		if (top.y == 1) {
			top.y = 0;
		}
		if (right.x == pImage.getWidth() - 1) {
			right.x = pImage.getWidth();
		}
		if (buttom.y == pImage.getHeight() - 1) {
			buttom.y = pImage.getHeight();
		}
		return new Point[] { left, top, right, buttom };

	}

	public static Point getCornerLeft(BufferedImage pImage, Color findColor,
			boolean match) {
		return findColor(pImage, new Point(0, 0), findColor, 0,
				Direction.VERTICAL, true, match);
	}

	private static Point getCornerRight(BufferedImage pImage, Color findColor,
			boolean match) {
		return findColor(pImage, new Point(pImage.getWidth(), 0), findColor, 0,
				Direction.VERTICAL, false, match);
	}

	private static Point getCornerTop(BufferedImage pImage, Color findColor,
			boolean match) {
		return findColor(pImage, new Point(0, 0), findColor, 0,
				Direction.HORICONTAL, true, match);
	}

	private static Point getCornerButtom(BufferedImage pImage, Color findColor,
			boolean match) {
		return findColor(pImage, new Point(0, pImage.getHeight()), findColor,
				0, Direction.HORICONTAL, false, match);
	}

	public static BufferedImage cutImage(BufferedImage pImage, Color bgColor,
			Color mainColor) {
		CaptchaBreaker.testWriteImage(pImage, "c:\\temp\\cutInput.gif");
		Point[] corners = getCorners(pImage, bgColor, mainColor);
		int width = corners[2].x - corners[0].x;
		int height = corners[3].y - corners[1].y;
		BufferedImage cuttedImage = new BufferedImage(width, height,
				pImage.getType());
		cuttedImage.createGraphics().drawImage(
				pImage.getSubimage(corners[0].x, corners[1].y, width, height),
				0, 0, null);

		CaptchaBreaker.testWriteImage(cuttedImage, "c:\\temp\\cutOutput.gif");
		return cuttedImage;
	}

	public static BufferedImage rotateImage(BufferedImage pImage) {

		double winkel = calculateWinkelToRotate(pImage);

		BufferedImage rotateImg = pImage.getSubimage(0, 0, pImage.getWidth(),
				pImage.getHeight());
		if (winkel != 0) {

			AffineTransform transform = new AffineTransform();
			transform.rotate(winkel, rotateImg.getWidth() / 2,
					rotateImg.getHeight() / 2);
			AffineTransformOp op = new AffineTransformOp(transform,
					AffineTransformOp.TYPE_BILINEAR);
			rotateImg = op.filter(rotateImg, null);
		}

		return rotateImg;
	}

	public static BufferedImage loadImage(File file) {
		try {
			FileInputStream fis = new FileInputStream(file);
			BufferedImage image;
			image = ImageIO.read(fis);
			return image;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	public static BufferedImage loadImage(String path) {
		return loadImage(new File(path));
	}

	// center_letter_A_0.gif
	// center_number_14_0.gif
	// bg_number_A_0.gif
	// bg_letter_14_0.gif

	private static String getValueInCompareTemplateFile(File file) {
		String tmp = file.getName();
		String result = "";

		if (tmp.contains("center")) {
			if (tmp.contains("letter")) {
				result = tmp.substring(tmp.indexOf("letter_")
						+ "letter_".length());
				result = result.substring(0, 1);
			}
			if (tmp.contains("number")) {
				result = tmp.substring(tmp.indexOf("number_")
						+ "number_".length());
				result = result.substring(0, result.indexOf("_"));
			}
		} else if (tmp.contains("small")) {
			if (tmp.contains("number")) {
				result = tmp.substring(tmp.indexOf("number_")
						+ "number_".length());
				result = result.substring(0, result.indexOf("_"));
			}
		}
		return result;
	}

	public static CompareImage getBestMatch(BufferedImage compareTo,
			String compDir, int pToleranz) {
		File file = new File(compDir.toString());
		File[] compareListFiles = file.listFiles();
		CompareImage returnImage = null;

		long result = 99999;
		long diff = 0;
		System.out.print("Compare: ");
		for (int i = 0; i < compareListFiles.length - 1; i++) {
			String valueOfImage = getValueInCompareTemplateFile(compareListFiles[i]);
			// System.out.print(compareListFiles[i].getName());
			System.out.print("#");

			BufferedImage compImage = EasyImage.loadImage(compareListFiles[i]);
			diff = EasyImage.compare(compImage, compareTo, pToleranz);
			// System.out.println(" diff: " + diff);// + " (until " +
			// valueOfImage + ")");
			if (diff < result) {

				returnImage = new CompareImage(compImage, compareListFiles[i],
						valueOfImage);
				result = diff;
			}

		}
		System.out.println();
		// System.out.println("BestMatch: " + returnImage.getValue());
		return returnImage;

	}

	public static long compare(String img1, String img2, int pToleranz) {
		return compare(loadImage(img1), loadImage(img2), pToleranz);
	}

	public static long compare(BufferedImage image1, BufferedImage image2,
			int pToleranz) {
		long diffPixels = 0;
		int rotRGB = Color.RED.getRGB();
		int pixel1;
		int pixel2;
		BufferedImage[] sameSize = makeSameSize(image1, image2);
		image1 = sameSize[0];
		image2 = sameSize[1];
		BufferedImage diffImage = new BufferedImage(image1.getWidth(),
				image1.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
		int breite1 = image1.getWidth(null);
		int hoehe1 = image1.getHeight(null);
		CaptchaBreaker.testWriteImage(image1, "C:\\temp\\compareTest.gif");
		CaptchaBreaker.testWriteImage(image2, "C:\\temp\\compareSearch.gif");

		for (int j = 0; j < hoehe1; j++) {
			for (int i = 0; i < breite1; i++) {
				pixel1 = image1.getRGB(i, j);
				pixel2 = image2.getRGB(i, j);

				if (!EasyImage.matchColor(pixel1, pixel2, pToleranz)) {
					diffImage.setRGB(i, j, rotRGB);
					diffPixels++;
				}
			}
		}
		return diffPixels;
	}

	public static BufferedImage[] makeSameSize(BufferedImage image1,
			BufferedImage image2) {

		Point newSize = new Point();
		newSize.x = image1.getWidth() > image2.getWidth() ? image1.getWidth()
				: image2.getWidth();
		newSize.y = image1.getHeight() > image2.getHeight() ? image1
				.getHeight() : image2.getHeight();

		BufferedImage scaledImage1 = new BufferedImage(newSize.x, newSize.y,
				BufferedImage.TYPE_BYTE_GRAY);
		// Paint scaled version of image to new image
		Graphics2D graphics2D1 = scaledImage1.createGraphics();
		graphics2D1.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		graphics2D1.drawImage(image1, 0, 0, newSize.x, newSize.y, null);
		graphics2D1.dispose();

		BufferedImage scaledImage2 = new BufferedImage(newSize.x, newSize.y,
				BufferedImage.TYPE_BYTE_GRAY);
		// Paint scaled version of image to new image
		Graphics2D graphics2D2 = scaledImage2.createGraphics();
		graphics2D2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
				RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		graphics2D2.drawImage(image2, 0, 0, newSize.x, newSize.y, null);

		// clean up
		graphics2D2.dispose();

		return new BufferedImage[] { scaledImage1, scaledImage2 };
	}

	public static double calculateWinkelToRotate(BufferedImage pImage) {
		// System.out.println("calculateWinkelForRotateImage...");
		Point topLeft = getCornerTop(
				pImage.getSubimage(0, 0, pImage.getWidth() / 2,
						pImage.getHeight()), Color.BLACK, false);

		Point topRight = getCornerTop(pImage.getSubimage(pImage.getWidth() / 2,
				0, pImage.getWidth() / 2, pImage.getHeight()), Color.BLACK,
				false);
		topRight.x += pImage.getWidth() / 2;

		double adx = Math.abs(topLeft.x - topRight.x);
		double ady = Math.abs(topLeft.y - topRight.y);
		double winkel;
		winkel = Math.tan(ady / adx);
		winkel = topLeft.y < topRight.y ? winkel * -1 : winkel;

		return winkel;
	}

	public static BufferedImage[] splitImageToLetterAndNumber(
			BufferedImage pInput, File pFile, Color mainColor,
			int splitXPosition, boolean editing) {
		Set<Color> oneColor = new HashSet<Color>();
		oneColor.add(mainColor);
		return splitImageToLetterAndNumber(pInput, oneColor, splitXPosition,
				editing);
	}

	public static StringIntegerPair getValuesOfCenterImage(
			BufferedImage pinput, File file) {

		StringIntegerPair result = EasyImage.getValueOfImage(pinput, file,
				EasyImage.COLOR_NUMBER_CENTER, CaptchaBreaker.PATH_TESTIMAGE
						+ "\\compareimages\\center\\letter",
				CaptchaBreaker.PATH_TESTIMAGE
						+ "\\compareimages\\center\\number",
				TOLERANZ_BESTMATCH_CENTER_IMAGE, true);

		return result;
	}

	/**
	 * who of this imagesToCheck contains the SearchValue?
	 * 
	 * @param imagesToCheck
	 * @param searchValue
	 * @return
	 */
	public static CompareImage getImageWithThisValue(
			List<CompareImage> imagesToCheck, final int searchValue) {

		if (searchValue == 0) {
			return imagesToCheck.get(0);
		}

		File file = new File(CaptchaBreaker.PATH_TESTIMAGE
				+ "\\compareimages\\small\\number");
		File[] compareListFiles = file.listFiles(new FileFilter() {
			@Override
			public boolean accept(File arg0) {
				return arg0.getName().startsWith("small_number_" + searchValue);
			}
		});
		CompareImage returnImage = null;

		long result = 99999;
		long diff = 0;
		Iterator<CompareImage> itrImages = imagesToCheck.iterator();

		while (itrImages.hasNext()) {
			CompareImage compareTo = itrImages.next();
			// System.out.println("############# Compare Image " + imgCol++);
			for (int i = 0; i < compareListFiles.length - 1; i++) {
				BufferedImage compImage = EasyImage
						.loadImage(compareListFiles[i]);
				diff = EasyImage.compare(compImage, compareTo.getImage(),
						EasyImage.TOLERANZ_BESTMATCH_SMALL_IMAGE);
				if (diff < result) {
					returnImage = compareTo;
					result = diff;
				}
				// System.out.println(compareListFiles[i] + " diff: " + diff);
			}
		}

//		System.out.println("Best Match: " + returnImage.getPosition());

		return returnImage;
	}

	private static StringIntegerPair getValueOfImage(BufferedImage pinput,
			File file, Set<Color> mainColors, String dirLetters,
			String dirNumbers, int splitXPosition, boolean editing) {

		BufferedImage[] splittedImages;
		BufferedImage tmpImage = beforeSplitImageToLetterAndNumber(pinput,
				mainColors, splitXPosition, editing);

		if (editing) {

			splittedImages = EasyImage.splitImageToLetterAndNumber(tmpImage,
					mainColors, splitXPosition, editing);

			CompareImage compResultLetter = EasyImage.getBestMatch(
					splittedImages[0], dirLetters, 100);

			CompareImage compResultNumber = EasyImage.getBestMatch(
					splittedImages[1], dirNumbers, 100);

			StringIntegerPair result = new StringIntegerPair(
					compResultLetter.getValue(),
					Integer.valueOf(compResultNumber.getValue()));
			if (file != null) {
				writeSplittedLetterImage(file, "center", splittedImages[0]);
				writeSplittedNumberImage(file, "center", splittedImages[1]);
			}
			return result;
		}
		writeSplittedNumberImage(file, "small", tmpImage);
		return null;
	}

	private static BufferedImage beforeSplitImageToLetterAndNumber(
			BufferedImage input, Set<Color> mainColors, int splitXPosition,
			boolean BigCenterLetter) {

		BufferedImage isolatedImage = input;
		BufferedImage readyImage = isolatedImage;
		if (BigCenterLetter) {
			isolatedImage = EasyImage.isolateImage(input, mainColors,
					Color.BLACK, Color.WHITE, 10, BigCenterLetter);
		} else {
			isolatedImage = EasyImage.isolateImage(input, COLOR_NUMBER_SMALL,
					Color.WHITE, Color.BLACK, 0, false);
		}

		CaptchaBreaker.testWriteImage(isolatedImage,
				"C:\\temp\\split1IsoLated.gif");

		isolatedImage = EasyImage.cutImage(isolatedImage, Color.BLACK,
				Color.WHITE);
		CaptchaBreaker.testWriteImage(isolatedImage, "C:\\temp\\split2cut.gif");
		readyImage = EasyImage.rotateImage(isolatedImage);

		CaptchaBreaker
				.testWriteImage(readyImage, "C:\\temp\\split3rotated.gif");

		readyImage = EasyImage.cutImage(readyImage, Color.BLACK, Color.WHITE);
		CaptchaBreaker.testWriteImage(readyImage, "C:\\temp\\split5cutted.gif");
		return readyImage;

	}

	private static void writeSplittedLetterImage(File file, String prefix,
			BufferedImage letter) {
		String fileNameLetter = file.getAbsolutePath().substring(0,
				file.getAbsolutePath().length() - file.getName().length());
		fileNameLetter += prefix + "_letter_" + file.getName().substring(0, 1)
				+ file.getName().substring(file.getName().length() - 4);
		fileNameLetter = getNextName(fileNameLetter);
		CaptchaBreaker.testWriteImage(letter, fileNameLetter);
	}

	private static void writeSplittedNumberImage(File file, String prefix,
			BufferedImage number) {

		String fileNameNumber = file.getAbsolutePath().substring(0,
				file.getAbsolutePath().length() - file.getName().length());
		fileNameNumber += prefix + "_number_"
				+ file.getName().substring(1, file.getName().indexOf("_"))
				+ file.getName().substring(file.getName().length() - 4);
		fileNameNumber = getNextName(fileNameNumber);

		CaptchaBreaker.testWriteImage(number, fileNameNumber);
	}

	public static BufferedImage[] splitImageToLetterAndNumber(
			BufferedImage input, Set<Color> mainColors, int splitXPosition,
			boolean BigCenterLetter) {

		int splitX = EasyImage.splitLetterNumber(input, splitXPosition,
				Color.BLACK, 1);

		BufferedImage letter = null;
		BufferedImage number = null;

		// --------------- Letter
		letter = input.getSubimage(0, 0, splitX, input.getHeight());
		letter = EasyImage.cutImage(letter, Color.BLACK, Color.WHITE);
		letter = EasyImage.fillLeaks(letter, Color.WHITE, Color.BLACK,
				Direction.VERTICAL, 8, 1);
		letter = EasyImage.fillLeaks(letter, Color.WHITE, Color.BLACK,
				Direction.HORICONTAL, 8, 1);
		// -------------- Number

		number = input.getSubimage(splitX, 0, input.getWidth() - splitX,
				input.getHeight());
		number = EasyImage.cutImage(number, Color.BLACK, Color.WHITE);
		number = EasyImage.fillLeaks(number, Color.WHITE, Color.BLACK,
				Direction.VERTICAL, 8, 1);
		number = EasyImage.fillLeaks(number, Color.WHITE, Color.BLACK,
				Direction.HORICONTAL, 8, 1);

		return new BufferedImage[] { letter, number };
	}

	public static void fill8(BufferedImage inputImage, int x, int y,
			int alteFarbe, int neueFarbe, boolean sameColor) {
		if (x >= inputImage.getWidth() || y >= inputImage.getHeight() || x < 0
				|| y < 0) {
			return;
		}
		boolean action = (((inputImage.getRGB(x, y) == alteFarbe) && sameColor) || (inputImage
				.getRGB(x, y) != alteFarbe) && !sameColor);

		if (action) {

			inputImage.setRGB(x, y, neueFarbe);
			fill8(inputImage, x, y + 1, alteFarbe, neueFarbe, sameColor); // unten
			fill8(inputImage, x, y - 1, alteFarbe, neueFarbe, sameColor); // oben
			fill8(inputImage, x + 1, y, alteFarbe, neueFarbe, sameColor); // rechts
			fill8(inputImage, x - 1, y, alteFarbe, neueFarbe, sameColor); // links
			fill8(inputImage, x + 1, y + 1, alteFarbe, neueFarbe, sameColor); // unten-rechts
			fill8(inputImage, x + 1, y - 1, alteFarbe, neueFarbe, sameColor); // oben-rechts
			fill8(inputImage, x - 1, y + 1, alteFarbe, neueFarbe, sameColor); // unten-links
			fill8(inputImage, x - 1, y - 1, alteFarbe, neueFarbe, sameColor); // oben-links

		}
		return;
	}

	public static void fill8(BufferedImage inputImage, int x, int y,
			int alteFarbe, int neueFarbe) {
		fill8(inputImage, x, y, alteFarbe, neueFarbe, true);
	}

	private static String getNextName(String namePfad) {
		String newName = namePfad.substring(0, namePfad.indexOf(".gif"));
		String ext = namePfad.substring(namePfad.length() - 4);
		int i = 0;
		while (new File(newName + "_" + i + ext).exists()) {
			i++;
		}
		newName += "_" + i + ext;
		return newName;
	}

}
